using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class BuildScreen : MonoBehaviour {
	
	// Public parameters.
	public Camera worldCamera;
	public Camera buildCamera;
	public GameObject buildTargetLine;
	public GameObject buildScreenContainer;
	
	public Material previewMaterial;
	
	public Vector3 buildScreenInitialPosition = new Vector3(2, 2, 0);
	
	public string rotateWeaponX 		= "BuildRotateWeaponX";
	public string rotateWeaponY 		= "BuildRotateWeaponY";
	public string rotateComponentX 		= "BuildRotateComponentX";
	public string rotateComponentY 		= "BuildRotateComponentY";
	public string moveComponentX 		= "BuildMoveComponentX";
	public string moveComponentY 		= "BuildMoveComponentY";
	public string lockComponentButton 	= "BuildLock";
	public string lootButton 			= "Loot";
	public string toggleBuildScreen		= "ToggleBuildScreen";
	public string switchWeapon	 		= "InventoryRotateLeft";
	public string switchComponent 		= "InventoryRotateRight";
	
	public float speed 					= 0.5f;
	public float minMovement 			= 0.1f;
	
	// Looting
	public float maxLootDist 			= 3.0f;
	
	// Private members.
	private EntityMgr entityMgr;
	private Entity weapon;
	private Entity component;
	private Entity preview;
	
	public void Start() {
		entityMgr = new EntityMgr(buildScreenContainer.transform);
	}
	
	public void removePreview() {
		if (preview != null) {
			preview.releaseLock();
			
			preview.each (delegate(GameObject obj) {
				Destroy(obj);
				return false;
			});

			preview = null;
		}
	}

	public void Update () {
		if (buildCamera.enabled) {
			if (Input.GetButtonDown(toggleBuildScreen)) {
				buildCamera.enabled = false;
				worldCamera.enabled = true;
				
				if (weapon != null) {
					entityMgr.fromBuildScreen(weapon);
					weapon = null;
				}
				
				if (component != null) {
					entityMgr.fromBuildScreen(component);
					component = null;
				}
				
				removePreview();
			} else {
				LineRenderer renderer = null;
				
				if (buildTargetLine != null) {
					buildTargetLine.GetComponent<LineRenderer>();
				}
				
				if (preview != null) {
					preview.releaseLock();
				}
				
				// TODO if component is last item, make it weapon.
				// TODO rotate around real center and not root center.
				
				// Rotate weapon
				if (weapon != null) {
					
					/*if (Input.GetButtonDown(switchWeapon)) {
						entityMgr.fromBuildScreen(weapon);
						weapon = entityMgr.toBuildScreen(Vector3.zero);
					}
					
					if (Input.GetButtonDown(switchComponent)) {
						entityMgr.fromBuildScreen(component);
						component = entityMgr.toBuildScreen(buildScreenInitialPosition);
					}*/
					
					getJoystick(rotateWeaponX, rotateWeaponY, delegate(Vector3 dir) {
						weapon.transform.RotateAroundLocal(Vector3.up, -dir.x * speed);
						weapon.transform.RotateAroundLocal(Vector3.right, -dir.y * speed);
					});
					
					// Rotate and move component
					if (component != null) {
						// Find the SnapLock.
						//SnapLock snapLock = component.GetComponent<SnapLock>();

						// If the component is locked, null it so that a new one can take its place.
						if (component.isLocked()) {
							Debug.Log("Component is already locked, even though it should not be. It will be removed the next step.");
							component = null;
							removePreview();
							
						// The component is not locked.
						} else {
						
							// Handle rotation
							getJoystick(rotateComponentX, rotateComponentY, delegate(Vector3 dir) {
								component.transform.RotateAroundLocal(Vector3.up, -dir.x * (speed / 4.0f));
								component.transform.RotateAroundLocal(Vector3.right, -dir.y * (speed / 4.0f));
							});
							
							// Handle movement
							getJoystick(moveComponentX, moveComponentY, delegate(Vector3 dir) {
								Vector3 delta = buildCamera.cameraToWorldMatrix * dir * speed;
								component.transform.position += delta;
							});

							// Check for lock key.
							if (Input.GetButtonDown(lockComponentButton)) {
								component.lockTowards(weapon);
								component = null;
								removePreview();
							} else {
								if (preview == null) {
									GameObject temp = GameObject.Instantiate(component.gameObject) as GameObject;
									preview = temp.GetComponent<Entity>();
									preview.setMaterial(previewMaterial);
									//preview.findRenderer().material = previewMaterial;
								}
								
								preview.transform.position = component.transform.position;
								preview.transform.rotation = component.transform.rotation;
								preview.lockTowards(weapon);
								preview.updateLock();

								// Update ribbon location
								if (buildTargetLine != null) {
									Entity.Pair pair = component.findLocks(weapon);
									
									if (pair.isSet()) {
										renderer.enabled = true;
										renderer.SetPosition(0, pair.local.transform.position);
										renderer.SetPosition(1, pair.target.transform.position);
									} else {
										renderer.enabled = false;	
									}
								}
							}
						}

						
					
					// There are no component in the build screen. Try adding one.
					} else if (entityMgr.hasItem()) {
						component = entityMgr.toBuildScreen(buildScreenInitialPosition);
					} else if (renderer != null) {
						renderer.enabled = false;
					}
					
				// There are no weapon in the build screen. Try adding one.
				} else if (entityMgr.hasItem()) {
					weapon = entityMgr.toBuildScreen(Vector3.zero);
				} else if (renderer != null) {
					renderer.enabled = false;
				}

				// TODO switch item
				// TODO switch weapon
			}
		} else {
			if (Input.GetButtonDown(toggleBuildScreen)) {
				buildCamera.enabled = true;
				worldCamera.enabled = false;
			} else {
				handleLoot();
			}
		}
	}
	
	private void handleLoot() {
		// If the player hits the loot button.
		if (Input.GetButtonDown(lootButton)) {
			// Find any item targeted.
			
			Ray ray = new Ray(worldCamera.transform.position, worldCamera.transform.forward);
			RaycastHit hit = new RaycastHit();
			
			if (Physics.Raycast(ray, out hit, maxLootDist)) {
				if (hit.collider.transform.parent != null) {
					Entity entity = hit.collider.transform.parent.GetComponent<Entity>();
					
					// If an item was found, check if it is a lootable item.
					if (entity != null) {
						// Find the GameObject and move it to the inventory.
						entityMgr.fromWorld(entity);
					}
				}
			}
		}
	}

	private void getJoystick(string inputX, string inputY, System.Action<Vector3> onInput) {
		var directionVector = new Vector3(
			Input.GetAxis(inputX), 
			Input.GetAxis(inputY), 
			0
		);
		
		if (directionVector.magnitude > minMovement) {
			// Get the length of the directon vector and then normalize it
			// Dividing by the length is cheaper than normalizing when we already have the length anyway
			float directionLength = directionVector.magnitude;
			directionVector = directionVector / directionLength;
			
			// Make sure the length is no bigger than 1
			directionLength = Mathf.Min(1, directionLength);
			
			// Make the input vector more sensitive towards the extremes and less sensitive in the middle
			// This makes it easier to control slow speeds when using analog sticks
			directionLength = directionLength * directionLength;
			
			// Multiply the normalized direction vector by the modified length
			directionVector = directionVector * directionLength;
			
			if (onInput != null) {
				onInput(directionVector);
			}
		}
	}
	
	/*private void updateSnapPoints() {
		if (weapon != null && component != null) {
			GameObject smallestA = null;
			GameObject smallestB = null;

			// Go through siblings.
			eachPointInGroup(weapon, delegate(GameObject pointA) {

				// Go through siblings.
				eachPointInGroup(component, delegate(GameObject pointB) {

					// If this pair is closer to each other than the cached,
					// replace the cache.
					if (smallestA == null 
					||  smallestB == null 
					||  distance(smallestA, smallestB) > distance(pointA, pointB)) {
						smallestA = pointA;
						smallestB = pointB;
					}
				});
			
			});
			
			// Make sure a pair was found.
			if (smallestA != null 
			&&  smallestB != null) {
				// Create the SnapLock script if it does not exist.
				SnapLock snapLock = component.GetComponent<SnapLock>();
				
				if (snapLock == null) {
					Debug.Log("Creating missing SnapLock...");
					snapLock = component.AddComponent<SnapLock>();
				}

				// Store the shortest pair.
				snapLock.setTargetSnapPoint(smallestA);
				snapLock.setLocalSnapPoint(smallestB);
				
			} else {
				Debug.Log("Found no pair.");
			}
		} else {
			Debug.Log("Weapon or Component is null when snap points are beeing updated.");	
		}
	}*/
	
//	private void eachPointInGroup(GameObject obj, System.Action<GameObject> f) {
//		// TODO Not all entities in group are checked.
//		
//		
//		// Go through siblings.
//		//each(root.transform.parent, delegate(GameObject sibling) {
//			
//			// Go through all the siblings points.
//			each(/*sibling.transform*/obj.transform, delegate(GameObject point) {
//			
//				// Make sure the point is a point.
//				if (isPoint(point)) {
//					
//					// Callback.
//					f(point);
//					
//				}
//			});
//		//});
//	}
	
//	private bool isPoint(GameObject obj) {
//		return (obj.GetComponent<SnapPoint>() != null);
//	}
//	
//	private void each(Transform t, System.Action<GameObject> f) {
//		for (var i = 0; i < t.GetChildCount(); i += 1) {
//			f(t.GetChild(i).gameObject);
//		}
//	}
//	
//	private float distance(GameObject a, GameObject b) {
//		return (a.transform.position - b.transform.position).magnitude;
//	}
}
